﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JOATServer.SocietyFramework.Core.XML
{
    public abstract partial class BaseXMLAutomaton : BaseAutomaton
    {
        /* STATE / TRANSITION CLASSES */

        /// <summary>
        /// Extension of the State class which executes a state code in an XML automaton.
        /// </summary>
        protected class XMLState : State, CodeContainer
        {
            public Instruction instruction;

            public XMLState(BaseXMLAutomaton owner) : base(owner) { }

            public override void StateCode()
            {
                if (instruction != null)
                {
                    instruction.Code();
                }
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.instruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock((BaseXMLAutomaton)Owner);
                        instructionBlock.AddInstruction(this.instruction);
                        instructionBlock.AddInstruction(instruction);
                        this.instruction = instructionBlock;
                    }
                }
                else
                {
                    this.instruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)instruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.instruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.instruction;
                        this.instruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Extension of the Transition class which executes a transition in an XML automaton.
        /// </summary>
        protected class XMLTransition : Transition, CodeContainer
        {
            public Instruction instruction;

            public XMLTransition(BaseXMLAutomaton owner) : base(owner) { }

            public override string TransitionCode()
            {
                if (instruction != null)
                {
                    return (string)instruction.Code();
                }
                else
                {
                    return null;
                }
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.instruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock((BaseXMLAutomaton)Owner);
                        instructionBlock.AddInstruction(this.instruction);
                        instructionBlock.AddInstruction(instruction);
                        this.instruction = instructionBlock;
                    }
                }
                else
                {
                    this.instruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)instruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.instruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.instruction;
                        this.instruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Extension of the TransitionGroup class which executes a transition group in an XML automaton.
        /// </summary>
        protected class XMLTransitionGroup : TransitionGroup, CodeContainer
        {
            public Instruction instruction;

            public XMLTransitionGroup(BaseXMLAutomaton owner) : base(owner) { }

            public override string TransitionCode()
            {
                if (instruction != null)
                {
                    return (String)instruction.Code();
                }
                else
                {
                    return null;
                }
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.instruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock((BaseXMLAutomaton)Owner);
                        instructionBlock.AddInstruction(this.instruction);
                        instructionBlock.AddInstruction(instruction);
                        this.instruction = instructionBlock;
                    }
                }
                else
                {
                    this.instruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)instruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.instruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.instruction;
                        this.instruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }




        /* ******************** INSTRUCTION CLASSES ******************** */

        public enum SpecialSymbols { BREAK, CONTINUE };

        /// <summary>
        /// Interface which is implemented by instructions in an XMLState or an XMLTransition.
        /// </summary>
        public interface Instruction
        {
            BaseXMLAutomaton Owner { get; set; }

            /// <summary>
            /// Function called when the instruction is executed
            /// </summary>
            /// <returns>the result of the instruction which can be any value.</returns>
            object Code();
        }

        /// <summary>
        /// Interface which a class can implement in order to mark that it contains code.
        /// </summary>
        public interface CodeContainer
        {
            /// <summary>
            /// Function that adds an instruction to the tail of the list of instructions in the implementing class.
            /// </summary>
            /// <param name="instruction">the instruction to be added</param>
            void AddInstruction(Instruction instruction);

            /// <summary>
            /// Function that removes an instruction from the tail of the list of instructions in the implementing class.
            /// </summary>
            /// <returns>the removed instruction</returns>
            Instruction RemoveInstruction();
        }

        /// <summary>
        /// Class which represents a block of instructions in an XMLState or an XMLTransition.
        /// </summary>
        protected class InstructionBlock : Instruction, CodeContainer
        {
            public BaseXMLAutomaton Owner { get; set; }
            public List<Instruction> Instructions { get; set; }

            public InstructionBlock(BaseXMLAutomaton owner)
            {
                this.Instructions = new List<Instruction>();
                this.Owner = owner;
            }

            /// <summary>
            /// Function called when the instruction block is executed. Execution is terminated when an instruction returns a value.
            /// </summary>
            /// <returns>This value can be null (which represents continuing the execution with the next instruction), a returned
            /// value or a LoopSpecialSymbols.BREAK/LoopSpecialSymbols.CONTINUE symbol which will be interpreted by the loop that
            /// contains the block.</returns>
            public object Code()
            {
                object returnedValue = null;
                foreach (Instruction i in Instructions)
                {
                    returnedValue = i.Code();
                    if (returnedValue != null) break;
                }
                return returnedValue;
            }

            public void AddInstruction(Instruction instruction)
            {
                Instructions.Add(instruction);
            }

            public Instruction RemoveInstruction()
            {
                int index = Instructions.Count - 1;
                Instruction i = Instructions[index];
                Instructions.RemoveAt(index);
                return i;
            }
        }
    }
}
